{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "pnDoQjm8FFG4"
   },
   "source": [
    "##### Copyright 2020 The Cirq Developers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "cellView": "form",
    "id": "fPu5J-mrFGdR"
   },
   "outputs": [],
   "source": [
    "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "# https://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "dRLVi78IEmoP"
   },
   "source": [
    "# Quantum Computing Service reservation utility"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "QZAoWTSdFKgC"
   },
   "source": [
    "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://www.example.org/cirq/tutorials/google/reservations\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />View on QuantumLib</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/quantumlib/Cirq/blob/master/docs/tutorials/google/reservations.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://github.com/quantumlib/Cirq/blob/master/docs/tutorials/google/reservations.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a href=\"https://storage.googleapis.com/tensorflow_docs/Cirq/docs/tutorials/google/reservations.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Download notebook</a>\n",
    "  </td>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "id": "846b32703c5c"
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    import cirq\n",
    "except ImportError:\n",
    "    print(\"installing cirq...\")\n",
    "    !pip install --quiet cirq\n",
    "    print(\"installed cirq.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "D1o2qv7Ha1IT"
   },
   "source": [
    "This Colab notebook provides canned interactions with Cirq to manage a project's reservations on the Quantum Computing Service.\n",
    "\n",
    "For information on how to download a Colab notebook from GitHub, see [these instructions](colab.ipynb).  You can also [view this file](https://github.com/quantumlib/Cirq/blob/master/docs/tutorials/google/reservations.ipynb) or download the [raw content](https://raw.githubusercontent.com/quantumlib/Cirq/master/docs/tutorials/google/reservations.ipynb) of this content from Github."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "7z1RYfLob8dW"
   },
   "source": [
    "## Configure\n",
    "\n",
    "Choose the project to manage, autheticate, and install the necessary tools.\n",
    "\n",
    "**Note: If you are running on Jupyter notebook, please don't forget to change `project_id`, `processor_id` and `time_zone` to your preferences** "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "cellView": "form",
    "id": "YoqI9GrOPEx2"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Notebook is not executed with Colab, assuming Application Default Credentials are setup.\n"
     ]
    }
   ],
   "source": [
    "def authenticate_user():\n",
    "  \"\"\"Runs the user through the Colab OAuth process.\n",
    "  \n",
    "  Checks for Google Application Default Credentials and runs interactive login \n",
    "  if the notebook is executed in Colab. In case the notebook is executed in Jupyter notebook\n",
    "  or other IPython runtimes, no interactive login is provided, it is assumed that the \n",
    "  `GOOGLE_APPLICATION_CREDENTIALS` env var is set or `gcloud auth application-default login`\n",
    "  was executed already.\n",
    "  \n",
    "  For more information on using Application Default Credentials see \n",
    "  https://cloud.google.com/docs/authentication/production\n",
    "  \"\"\"\n",
    "  in_colab = False\n",
    "  try:\n",
    "     from IPython import get_ipython\n",
    "     in_colab = 'google.colab' in str(get_ipython())\n",
    "  except: \n",
    "     # Notebook is not executed within IPython. Assuming external authentication.\n",
    "     return \n",
    "\n",
    "  if in_colab: \n",
    "      from google.colab import auth      \n",
    "      print(\"Getting OAuth2 credentials.\")\n",
    "      print(\"Press enter after entering the verification code.\")\n",
    "      auth.authenticate_user(clear_output=False)\n",
    "      print(\"Authentication complete.\")\n",
    "  else: \n",
    "      print(\"Notebook is not executed with Colab, assuming Application Default Credentials are setup.\") \n",
    "\n",
    "authenticate_user()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "cellView": "form",
    "id": "YoqI9GrOPEx4"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Successful authentication using project balintp-gcp-lab!\n",
      "============================\n",
      "Runtime setup completed\n"
     ]
    }
   ],
   "source": [
    "import cirq.google\n",
    "from cirq.google.engine.client.quantum_v1alpha1.gapic import enums\n",
    "\n",
    "# The Google Cloud Project id to use.\n",
    "project_id = '' #@param {type:\"string\"}\n",
    "# The processor to view.\n",
    "processor_id = 'mcgee' #@param ['rainbow', 'pacific', 'mcgee']\n",
    "# The local time zone.\n",
    "time_zone = 'America/Los_Angeles'  #@param {type:\"string\"}\n",
    "\n",
    "if project_id == '':\n",
    "    import os \n",
    "    if 'GOOGLE_CLOUD_PROJECT' not in os.environ:\n",
    "        raise Exception(\"Please setup project_id in this cell or set the `GOOGLE_CLOUD_PROJECT` env var to your project id.\")\n",
    "    project_id = os.environ['GOOGLE_CLOUD_PROJECT']\n",
    "else: \n",
    "    os.environ['GOOGLE_CLOUD_PROJECT'] = project_id\n",
    "\n",
    "\n",
    "\n",
    "# Create an Engine object to use, providing the project id and the args\n",
    "try: \n",
    "    engine = cirq.google.get_engine()\n",
    "    engine.list_processors()\n",
    "    print(f\"Successful authentication using project {project_id}!\")\n",
    "except DefaultCredentialsError as err: \n",
    "    print(\"Could not authenticate to Google Quantum Computing Service.\")\n",
    "    print(\" Tips: If you are using Colab: make sure the previous cell was executed successfully.\")\n",
    "    print(\"       If this notebook is not in Colab (e.g. Jupyter notebook), make sure gcloud is installed and `gcloud auth application-default login` was executed.\")\n",
    "    print()\n",
    "    print(\"Error message:\")\n",
    "    print(err)\n",
    "except PermissionDenied as err:\n",
    "    print(f\"While you are authenticated to Google Cloud it seems the project '{project_id}' does not exist or does not have the Quantum Engine API enabled.\")\n",
    "    print(\"Error message:\")\n",
    "    print(err)\n",
    "processor = engine.get_processor(processor_id)\n",
    "\n",
    "import datetime\n",
    "import pytz\n",
    "tz = pytz.timezone(time_zone)\n",
    "now = tz.localize(datetime.datetime.now())\n",
    "\n",
    "def date_string(timestamp):\n",
    "  if timestamp.seconds < 0:\n",
    "    return 'Beginning of time'\n",
    "  if timestamp.seconds > 4771848621:\n",
    "    return 'End of time'\n",
    "  time = datetime.datetime.fromtimestamp(timestamp.seconds).astimezone(tz)\n",
    "  return time.strftime('%Y-%m-%d %H:%M:%S')\n",
    "  \n",
    "def delta(start, end):\n",
    "  if start.seconds < 0 or end.seconds > 5000000000:\n",
    "    return \"∞\"\n",
    "  return \"{} hrs\".format((end.seconds - start.seconds) / (60 * 60))\n",
    "\n",
    "def time_slot_string(time_slot):\n",
    "  start = date_string(time_slot.start_time)\n",
    "  end = date_string(time_slot.end_time)\n",
    "  slot_type = cirq.google.engine.client.quantum_v1alpha1.types.QuantumTimeSlot.TimeSlotType.Name(time_slot.slot_type)\n",
    "  slot_string = \"{} to {} ({}) - {}\".format(start, end, delta(time_slot.start_time, time_slot.end_time), slot_type)\n",
    "  if time_slot.HasField('reservation_config'):\n",
    "    return \"{} for {}\".format(slot_string, time_slot.reservation_config.project_id) \n",
    "  if time_slot.HasField('maintenance_config'):\n",
    "    return \"{} {} - {}\".format(slot_string, time_slot.maintenance_config.title, time_slot.maintenance_config.description) \n",
    "  return slot_string\n",
    "\n",
    "def reservation_string(reservation):\n",
    "  start = date_string(reservation.start_time)\n",
    "  end = date_string(reservation.end_time)\n",
    "  id = reservation.name.split('/reservations/')[1]\n",
    "  reservation_string = \"{} to {} ({}) - {}\".format(start, end, delta(reservation.start_time, reservation.end_time), id)\n",
    "  if len(reservation.whitelisted_users) > 0:\n",
    "    return \"{} {}\".format(reservation_string, reservation.whitelisted_users)\n",
    "  return reservation_string \n",
    "\n",
    "print(\"============================\")\n",
    "print(\"Runtime setup completed\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "riEmJXOCi5lK"
   },
   "source": [
    "## Checkout the upcoming schedule"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "cellView": "form",
    "id": "hFsclRLBwrpr"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2020-10-18 00:00:00 to 2020-11-06 14:00:00 (471.0 hrs) - OPEN_SWIM\n",
      "2020-11-06 14:00:00 to End of time (∞) - UNALLOCATED\n"
     ]
    }
   ],
   "source": [
    "#@title\n",
    "schedule = processor.get_schedule()\n",
    "for s in schedule:\n",
    "  print(time_slot_string(s))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "q0ud21aowieD"
   },
   "source": [
    "## Find available time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "cellView": "form",
    "id": "pNGFoBMm1TgK"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2020-11-06 14:00:00 to End of time (∞) - UNALLOCATED\n"
     ]
    }
   ],
   "source": [
    "#@title\n",
    "schedule = processor.get_schedule()\n",
    "unallocated = list(filter(lambda t: t.slot_type == enums.QuantumTimeSlot.TimeSlotType.UNALLOCATED, schedule))\n",
    "for s in unallocated:\n",
    "  print(time_slot_string(s))\n",
    "\n",
    "if len(unallocated) == 0:\n",
    "  print(\"No available time slots\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "PH5HPWIilgHk"
   },
   "source": [
    "## List upcoming reservations for the project"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "cellView": "form",
    "id": "xLahT_dblq-j"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "No reservations for project <xyz>\n"
     ]
    }
   ],
   "source": [
    "#@title\n",
    "reservations = processor.list_reservations()\n",
    "\n",
    "for r in reservations:\n",
    "  print(reservation_string(r))\n",
    "else: \n",
    "  print(f\"No reservations for project {project_id}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "nTjE4odUs5xL"
   },
   "source": [
    "## Reserve time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "cellView": "form",
    "id": "AB4DbjPbuk7E"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "#@markdown Create a new reservation for the given start date and time with the given duration in hours.\n",
    "start_date_picker = \"2020-11-06\" #@param {type:\"date\"}\n",
    "start_time = \"15\" #@param [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]\n",
    "hours =  1 #@param {type:\"integer\"}\n",
    "#@markdown Comma-separated email addresses of any additional users to explicitly whitelist for the reservation.\n",
    "whitelist_user_emails = \"\" #@param {type:\"string\"}\n",
    "\n",
    "start_time_naive = datetime.datetime.strptime(\"{} {}\".format(start_date_picker, start_time), '%Y-%m-%d %H')\n",
    "start_time = tz.localize(start_time_naive)\n",
    "end_time = start_time + datetime.timedelta(hours=hours)\n",
    "\n",
    "print(reservation_string(processor.create_reservation(start_time=start_time,\n",
    "                                   end_time=end_time,\n",
    "                                   whitelisted_users=[e.strip() for e in whitelist_user_emails.split(',') if e])))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ECCLMU4Bu-aF"
   },
   "source": [
    "## Update reservation time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "cellView": "form",
    "id": "DJdEDa_kvDlo"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "#@markdown Update the deatils of an existing reservation. _You can find the `reservation_id` by listing your reservations or checking the output when you create a new reservation._\n",
    "reservation_id = \"\" #@param {type:\"string\"}\n",
    "start_date_picker = \"2020-03-27\" #@param {type:\"date\"}\n",
    "start_time = \"12\" #@param [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]\n",
    "hours =  2#@param {type:\"integer\"}\n",
    "\n",
    "start_time_naive = datetime.datetime.strptime(\"{} {}\".format(start_date_picker, start_time), '%Y-%m-%d %H')\n",
    "start_time = tz.localize(start_time_naive)\n",
    "end_time = start_time + datetime.timedelta(hours=hours)\n",
    "\n",
    "print(reservation_string(processor.update_reservation(reservation_id=reservation_id,\n",
    "                                   start_time=start_time,\n",
    "                                   end_time=end_time)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zFi797gdyFuJ"
   },
   "source": [
    "## Update reservation whitelisted users"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "cellView": "form",
    "id": "8HlzMB2nyIP7"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "#@markdown Update the deatils of an existing reservation. _You can find the `reservation_id` by listing your reservations or checking the output when you create a new reservation._\n",
    "reservation_id = \"\" #@param {type:\"string\"}\n",
    "#@markdown Comma-separated email addresses of any additional users to explicitly whitelist for the reservation.\n",
    "whitelisted_user_emails = \"\" #@param {type:\"string\"}\n",
    "\n",
    "print(reservation_string(processor.update_reservation(reservation_id=reservation_id,\n",
    "                                   whitelisted_users=[e.strip() for e in whitelisted_user_emails.split(',') if e])))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "RFx1yhDYtOuJ"
   },
   "source": [
    "## Remove reservation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "cellView": "form",
    "id": "qvqIyUGBv3Hy"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "#@markdown Delete a specific reservation as long as it is outside the schedule freeze. Inside the schedule freeze period reservations are cancelled instead.\n",
    "reservation_id = \"\" #@param {type:\"string\"}\n",
    "\n",
    "processor.remove_reservation(reservation_id)"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "name": "reservations.ipynb",
   "toc_visible": true
  },
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
